package com.bff.gaia.streaming.api.xjoin.core.classloader;/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.CompoundEnumeration;

import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandlerFactory;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashSet;

/**
 * Project Name: gaia-parent
 * Description:
 * Data: 2019/7/23 11:28
 * Company: NEU
 *
 * @author tiger
 * @version v1.0
 */
public class DtClassLoader extends URLClassLoader {

	private static Logger log = LoggerFactory.getLogger(DtClassLoader.class);

	private static final String CLASS_FILE_SUFFIX = ".class";

	/**
	 * The parent class loader.
	 */
	protected ClassLoader parent;

	private boolean hasExternalRepositories = false;

	public DtClassLoader(URL[] urls, ClassLoader parent) {
		super(urls, parent);
		this.parent = parent;
	}

	public DtClassLoader(URL[] urls) {
		super(urls);
	}

	public DtClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
		super(urls, parent, factory);
	}

	@Override
	public Class<?> loadClass(String name) throws ClassNotFoundException {
		return this.loadClass(name, false);
	}

	@Override
	protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
		synchronized (getClassLoadingLock(name)) {
			if (log.isDebugEnabled()){
				log.debug("loadClass(" + name + ", " + resolve + ")");
			}
			Class<?> clazz = null;

			// (0.1) Check our previously loaded class cache
			clazz = findLoadedClass(name);
			if (clazz != null) {
				if (log.isDebugEnabled()){
					log.debug("  Returning class from cache");
				}
				if (resolve){
					resolveClass(clazz);
				}
				return (clazz);
			}

			// (2) Search local repositories
			if (log.isDebugEnabled()){
				log.debug("  Searching local repositories");
			}
			try {
				clazz = findClass(name);
				if (clazz != null) {
					if (log.isDebugEnabled()){
						log.debug("  Loading class from local repository");
					}
					if (resolve){
						resolveClass(clazz);
					}
					return (clazz);
				}
			} catch (ClassNotFoundException e) {
				// Ignore
			}

			if (log.isDebugEnabled()){
				log.debug("  Delegating to parent classloader at end: " + parent);
			}

			try {
				clazz = Class.forName(name, false, parent);
				if (clazz != null) {
					if (log.isDebugEnabled()){
						log.debug("  Loading class from parent");
					}
					if (resolve){
						resolveClass(clazz);
					}
					return (clazz);
				}
			} catch (ClassNotFoundException e) {
				// Ignore
			}
		}

		throw new ClassNotFoundException(name);
	}


	@Override
	public URL getResource(String name) {

		if (log.isDebugEnabled()){
			log.debug("getResource(" + name + ")");
		}

		URL url = null;

		// (2) Search local repositories
		url = findResource(name);
		if (url != null) {
			if (log.isDebugEnabled()){
				log.debug("  --> Returning '" + url.toString() + "'");
			}
			return (url);
		}

		// (3) Delegate to parent unconditionally if not already attempted
		url = parent.getResource(name);
		if (url != null) {
			if (log.isDebugEnabled()){
				log.debug("  --> Returning '" + url.toString() + "'");
			}
			return (url);
		}

		// (4) Resource was not found
		if (log.isDebugEnabled()){
			log.debug("  --> Resource not found, returning null");
		}
		return (null);
	}

	@Override
	public void addURL(URL url) {
		super.addURL(url);
		hasExternalRepositories = true;
	}

	/**
	 * FIXME 需要测试
	 * @param name
	 * @return
	 * @throws IOException
	 */
	@Override
	public Enumeration<URL> getResources(String name) throws IOException {
		@SuppressWarnings("unchecked")
		Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[1];
		tmp[0] = findResources(name);//优先使用当前类的资源

		if(!tmp[0].hasMoreElements()){//只有子classLoader找不到任何资源才会调用原生的方法
			return super.getResources(name);
		}

		return new CompoundEnumeration<>(tmp);
	}

	@Override
	public Enumeration<URL> findResources(String name) throws IOException {

		if (log.isDebugEnabled()){
			log.debug("findResources(" + name + ")");
		}

		LinkedHashSet<URL> result = new LinkedHashSet<>();

		Enumeration<URL> superResource = super.findResources(name);

		while (superResource.hasMoreElements()){
			result.add(superResource.nextElement());
		}

		// Adding the results of a call to the superclass
		if (hasExternalRepositories) {
			Enumeration<URL> otherResourcePaths = super.findResources(name);
			while (otherResourcePaths.hasMoreElements()) {
				result.add(otherResourcePaths.nextElement());
			}
		}

		return Collections.enumeration(result);
	}

}
